"use strict"
var QF = {
    animate(ele, cssObj, ease, time, callback) {
        var obj = {
            easeInQuad: function (x, t, b, c, d) {
                return c * (t /= d) * t + b;
            },
            easeOutQuad: function (x, t, b, c, d) {
                return -c * (t /= d) * (t - 2) + b;
            },
            easeInOutQuad: function (x, t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t + b;
                return -c / 2 * ((--t) * (t - 2) - 1) + b;
            },
            easeInCubic: function (x, t, b, c, d) {
                return c * (t /= d) * t * t + b;
            },
            easeOutCubic: function (x, t, b, c, d) {
                return c * ((t = t / d - 1) * t * t + 1) + b;
            },
            easeInOutCubic: function (x, t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t * t + b;
                return c / 2 * ((t -= 2) * t * t + 2) + b;
            },
            easeInQuart: function (x, t, b, c, d) {
                return c * (t /= d) * t * t * t + b;
            },
            easeOutQuart: function (x, t, b, c, d) {
                return -c * ((t = t / d - 1) * t * t * t - 1) + b;
            },
            easeInOutQuart: function (x, t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t * t * t + b;
                return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
            },
            easeInQuint: function (x, t, b, c, d) {
                return c * (t /= d) * t * t * t * t + b;
            },
            easeOutQuint: function (x, t, b, c, d) {
                return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
            },
            easeInOutQuint: function (x, t, b, c, d) {
                if ((t /= d / 2) < 1) return c / 2 * t * t * t * t * t + b;
                return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
            },
            easeInSine: function (x, t, b, c, d) {
                return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
            },
            easeOutSine: function (x, t, b, c, d) {
                return c * Math.sin(t / d * (Math.PI / 2)) + b;
            },
            easeInOutSine: function (x, t, b, c, d) {
                return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
            },
            easeInExpo: function (x, t, b, c, d) {
                return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
            },
            easeOutExpo: function (x, t, b, c, d) {
                return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
            },
            easeInOutExpo: function (x, t, b, c, d) {
                if (t == 0) return b;
                if (t == d) return b + c;
                if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
                return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
            },
            easeInCirc: function (x, t, b, c, d) {
                return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
            },
            easeOutCirc: function (x, t, b, c, d) {
                return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
            },
            easeInOutCirc: function (x, t, b, c, d) {
                if ((t /= d / 2) < 1) return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
                return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
            },
            easeInElastic: function (x, t, b, c, d) {
                var s = 1.70158; var p = 0; var a = c;
                if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3;
                if (a < Math.abs(c)) { a = c; var s = p / 4; }
                else var s = p / (2 * Math.PI) * Math.asin(c / a);
                return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
            },
            easeOutElastic: function (x, t, b, c, d) {
                var s = 1.70158; var p = 0; var a = c;
                if (t == 0) return b; if ((t /= d) == 1) return b + c; if (!p) p = d * .3;
                if (a < Math.abs(c)) { a = c; var s = p / 4; }
                else var s = p / (2 * Math.PI) * Math.asin(c / a);
                return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
            },
            easeInOutElastic: function (x, t, b, c, d) {
                var s = 1.70158; var p = 0; var a = c;
                if (t == 0) return b; if ((t /= d / 2) == 2) return b + c; if (!p) p = d * (.3 * 1.5);
                if (a < Math.abs(c)) { a = c; var s = p / 4; }
                else var s = p / (2 * Math.PI) * Math.asin(c / a);
                if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
                return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
            },
            easeInBack: function (x, t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                return c * (t /= d) * t * ((s + 1) * t - s) + b;
            },
            easeOutBack: function (x, t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
            },
            easeInOutBack: function (x, t, b, c, d, s) {
                if (s == undefined) s = 1.70158;
                if ((t /= d / 2) < 1) return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
                return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
            },
            easeInBounce: function (x, t, b, c, d) {
                return c - obj.easeOutBounce(x, d - t, 0, c, d) + b;
            },
            easeOutBounce: function (x, t, b, c, d) {
                if ((t /= d) < (1 / 2.75)) {
                    return c * (7.5625 * t * t) + b;
                } else if (t < (2 / 2.75)) {
                    return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
                } else if (t < (2.5 / 2.75)) {
                    return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
                } else {
                    return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
                }
            },
            easeInOutBounce: function (x, t, b, c, d) {
                if (t < d / 2) return obj.easeInBounce(x, t * 2, 0, c, d) * .5 + b;
                return obj.easeOutBounce(x, t * 2 - d, 0, c, d) * .5 + c * .5 + b;
            }
        }





        // 动画的实现思路： 开启一个定时器让定时器每20ms移动一次元素 
        // 问题：每一次移动元素的距离怎么算？
        // 一次移动的距离 =  总距离 / 总次数
        // 总距离 = 终点 - 起点
        // 总次数 = 总时间/定时器单次间隔时间

        // 定义单次时间
        var interval = 20;
        // 定义总次数   
        var allCount = time / interval;
        // 总距离应该使用终点 - 起点
        // 起点：就是元素现在的对应属性值 
        // 终点：cssObj里面装着
        // 因为用户cssObj里面不定装着几条数据 也不定是哪些属性 所以这里不可以定死
        var nowObj = {};
        for (var i in cssObj) {
            // i 就是用户传递的属性名
            // cssObj[i]就是终点
            nowObj[i] = parseFloat(QF.getCss(ele, i))
        }
        // cssObj里装着的是什么属性 nowObj里装着的就是这些属性的对应的现在值
        // nowObj =>  {left: xxx, top: xxx}

        // 定义变量保存步数
        var count = 0;
        var timer = setInterval(function () {
            // 定时器每执行一次 走了一步
            count++;
            // 在这里面让元素每一次都改变一点点 
            for (var i in cssObj) {
                var t = count * interval;
                var b = nowObj[i]
                var c = cssObj[i] - nowObj[i]
                var d = time
                var result = obj[ease](0, t, b, c, d)
                // 设置位置的时候要记得：元素是从当前位置开始移动 而不是只靠步长乘以步数
                if (i === "opacity") {
                    ele.style[i] = result;
                } else {
                    ele.style[i] = result + "px";
                }
            }

            // 判断count是否大于等于了allCount
            if (count >= allCount) {
                clearInterval(timer)
                // 因为用户不一定传递callback 所以我们就判断一下 保证在用户传递了的时候执行 不传递的时候就不执行
                if (callback) {
                    callback();
                }
                // 等价代码 等价于上面的if判断写法 逻辑与：前面为真 后面执行 前面不为真后面不执行
                // callback && callback()
            }

        }, interval)
    },

    getCss(ele, prop) {
        // 当前函数负责返回ele的prop属性
        // 判断方式有一个名称: 能力检测
        if (window.getComputedStyle) {
            // 说明当前的浏览器具备某些功能 
            return window.getComputedStyle(ele)[prop];
        } else {
            // 说明当前的浏览器不具备getComputedStyle  
            return ele.currentStyle[prop]
        }
    },

    get(url, data, callback) {

        // 假如用户传递的data是 字符串 直接放在url后面即可
        // 假如用户传递的data是 对象  我们需要帮助用户转为query字符串
        // {a:1, b:2, c:3} => a=1&b=2&c=3
        var querystring = '';
        if (typeof data === "string") {
            querystring = data;
        } else {
            querystring = QF.queryParse(data);
        }

        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4) {
                var obj = JSON.parse(xhr.responseText);
                callback(obj)
            }
        }
        xhr.open("GET", url + "?" + querystring, true)
        xhr.send()
    },

    post(url, data, callback) {
        var xhr = new XMLHttpRequest();
        var querystring = '';
        if (typeof data === "string") {
            querystring = data;
        } else {
            querystring = QF.queryParse(data);
        }
        xhr.onreadystatechange = function () {
            if (xhr.readyState === 4 && (xhr.status === 200 || xhr.status === 304)) {
                var obj = JSON.parse(xhr.responseText);
                callback(obj)
            }
        }
        xhr.open("POST", url, true);
        xhr.setRequestHeader("content-type", "application/x-www-form-urlencoded");
        xhr.send(querystring);
    },
    queryParse(data) {
        var querystring = '';
        for (var i in data) {
            // 第一次循环 i 是 a  data[i] 是 1 querystring += a=1&
            // 第二次循环 querystring => a=1&b=2& 
            // 第三次循环 querystring => a=1&b=2&c=3&
            querystring += i + "=" + data[i] + "&";
        }
        // 多了一个&就去掉
        querystring = querystring.slice(0, -1);
        return querystring
    },
    jsonp(url, data, callback) {
        // 获取callbackname值
        var callbackname = data.callbackname;
        // 1 格式化传递进来的数据
        let querystring = "";
        if (typeof data === "string") {
            querystring = data;
        } else {
            querystring = QF.queryParse(data);
        }
        // 2 创建script标签
        let script = document.createElement("script");
        // 3 设置src
        script.src = url + "?" + querystring;
        // 4 给全局添加一个fun函数  因为这个函数本身就是函数作用域 我们是没有办法在一个子作用域内合法的声明上级作用域变量的 但是可以添加属性 
        window[callbackname] = callback;
        // 小提示：script标签想要真的发送请求有两个条件 1 具备src 2 在树上
        // 5 上树
        document.body.appendChild(script);

        // 6 执行完毕之后要下树 要删除
        script.onload = function() {
            // 删除全局变量
            delete window[callbackname]
            // 下树
            document.body.removeChild(this)
        }

    }
}